package edu.csl.study.springboot.redis.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Component
public class RedisService {
	    @Autowired
	    private  StringRedisTemplate redisTemplate;

	    @Autowired
	    private ListOperations<String, String> listOperations;
	    
	    @Autowired
	    @Qualifier("redisTemplateForObject")
	    private  RedisTemplate<String, Object> redisTemplateForObject;
	    @Autowired
	    private  RedisDistributedLock redisDistributedLock;
	    /**
	     * 不设置过期时长
	     */
	    public static final long NOT_EXPIRE = -1;


	    /**
         * 发布订阅模式：发布消息。此方法集群中的多台机器会同时执行
         * @param channel
         * @param  message
         * @return
         */
	    public void publish(String channel,String message) {
	    	
	         redisTemplate.convertAndSend(channel, message);
	    }
        /**
         * 发布订阅模式：发布消息：发布消息（为了启动处理消息逻辑）并添加到消息队列中（为了保证只有一台机器能消费）
         * 此方法能保证只有一台机器执行
         * @param channel
         * @param message
         */
	    public void publishWithListLPush(String channel,String message ) {
	    	 listOperations.leftPush(channel, message);
	         redisTemplate.convertAndSend(channel, message);
	    }
	    
	    public boolean existsKey(String key) {
	    	return redisTemplate.hasKey(key);
	    }

	    /**
	     * 重名名key，如果newKey已经存在，则newKey的原值被覆盖
	     *
	     * @param oldKey
	     * @param newKey
	     */
	    public void renameKey(String oldKey, String newKey) {
	        redisTemplate.rename(oldKey, newKey);
	    }

	    /**
	     * newKey不存在时才重命名
	     *
	     * @param oldKey
	     * @param newKey
	     * @return 修改成功返回true
	     */
	    public boolean renameKeyNotExist(String oldKey, String newKey) {
	        return redisTemplate.renameIfAbsent(oldKey, newKey);
	    }

	    /**
	     * 删除key
	     *
	     * @param key
	     */
	    public void deleteKey(String key) {
	        redisTemplate.delete(key);
	    }

	    /**
	     * 删除多个key
	     *
	     * @param keys
	     */
	    public void deleteKey(String... keys) {
	        Set<String> kSet = Stream.of(keys).map(k -> k).collect(Collectors.toSet());
	        redisTemplate.delete(kSet);
	    }

	    /**
	     * 删除Key的集合
	     *
	     * @param keys
	     */
	    public void deleteKey(Collection<String> keys) {
	        Set<String> kSet = keys.stream().map(k -> k).collect(Collectors.toSet());
	        redisTemplate.delete(kSet);
	    }
	    
	    /**
	     * 设置key
	     *
	     * @param key
	     * @param value
	     */
	    public void setKey(String key,String value) {
	        redisTemplate.boundValueOps(key).set(value);
	    }
	    /**
	     * 设置key 和过期时间一起设置
	     * @param key
	     * @param value
	     * @param time
	     * @param timeUnit
	     */
	    public void setKey(String key,String value, long time, TimeUnit timeUnit) {
	        redisTemplate.boundValueOps(key).set(value, time, timeUnit);
	    }

		/**
		 * 获取缓存
		 * @param key
	 	*/
		public Object get(String key) {
			return key==null?null:redisTemplate.opsForValue().get(key);
		}

	    /**
	     * 设置key的生命周期
	     *
	     * @param key
	     * @param time
	     * @param timeUnit
	     */
	    public Boolean expireKey(String key, long time, TimeUnit timeUnit) {
	        return redisTemplate.expire(key, time, timeUnit);
	    }

	    /**
	     * 指定key在指定的日期过期
	     *
	     * @param key
	     * @param date
	     */
	    public void expireKeyAt(String key, Date date) {
	        redisTemplate.expireAt(key, date);
	    }

	    /**
	     * 查询key的生命周期
	     *
	     * @param key
	     * @param timeUnit
	     * @return
	     */
	    public long getKeyExpire(String key, TimeUnit timeUnit) {
	        return redisTemplate.getExpire(key, timeUnit);
	    }

	    /**
	     * 将key设置为永久有效
	     *
	     * @param key
	     */
	    public void persistKey(String key) {
	        redisTemplate.persist(key);
	    }
	    /**
	     * 存储数据或修改数据
	     * 
	     * @param modelMap
	     * @param mapName
	     */
	    public void setMap(String mapName, Map<String,  ? extends Object> modelMap) {
	        HashOperations<String, String, Object> hps = redisTemplateForObject.opsForHash();
	        hps.putAll(mapName, modelMap);
	    }
	    /**
	     * 如果不存在那么添加
	     * @param mapName
	     * @param hashKey
	     * @param hashKeyValue
	     */
	    public void putIfAbsent(String mapName, String hashKey, Object hashKeyValue) {
	        HashOperations<String, String, Object> hps = redisTemplateForObject.opsForHash();
	        hps.putIfAbsent(mapName, hashKey, hashKeyValue);
	    }
	    /**
	     * 直接添加
	     * @param mapName
	     * @param hashKey
	     * @param hashKeyValue
	     */
	    public void put(String mapName, String hashKey, Object hashKeyValue) {
	        HashOperations<String, String, Object> hps = redisTemplateForObject.opsForHash();
	        hps.put(mapName, hashKey, hashKeyValue);
	    }
	    /**
	     * 获取数据Map
	     * 
	     * @param mapName
	     * @return
	     */
	    public Map<String, Object> getMap(String mapName) {
	        HashOperations<String, String, Object> hps = redisTemplateForObject.opsForHash();
	        return hps.entries(mapName);

	    }
	    /**
	     * 获取数据value
	     * 
	     * @param mapName
	     * @param hashKey
	     * @return
	     */
	    public Object getMapValue(String mapName, String hashKey) {
	        HashOperations<String, String, Object> hps = redisTemplateForObject.opsForHash();
	        return hps.get(mapName, hashKey);

	    }
	    /**
	     * 批量删除缓存数据
	     * 
	     * @param keys
	     */
	    public void deleteMapByKeys(List<String> keys) {
	        // 执行批量删除操作时先序列化template
	    	redisTemplateForObject.setKeySerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
	    	redisTemplateForObject.delete(keys);
	    }
	    /**
	     * 分布式锁 - 加锁
	     * @param key  业务号相关的字符串
	     * @param requestID 先生成请求唯一标识，可以用UUID
	     *   目的：为了释放锁的时候，只能释放自己的锁而不能释放别人的锁。
	     *   场景：A任务在超时时间内没有完成任务，超时释放锁后B成功获取了锁，待A任务执行完成释放锁会释放B已获取的锁。
	     *   其实A还没有处理完成，B已经获取了锁，此时A和B是同时执行的，已经是异常的了！不让A释放B的锁，只是减少异常的进一步扩散。 
	     *   所以，过期时间expireSeconds一定要大于正常业务处理时间。当然太长了也不行，因为怕宕机等情况一直长时间占用锁影响业务。
	     * @param expireSeconds 必须要比正常业务时间长很多，但不能太长。
	     * @return
	     */
	    public boolean lock(String key,String requestID, long expireSeconds,String serverName) {
	    	
	 	   return redisDistributedLock.lock(key, requestID, expireSeconds, 0, 0,serverName);
	 	}
	    /**
	     * 分布式锁，加锁 并且有重试的功能
	     * @param key
	     * @param requestID
	     * @param expireSeconds
	     * @param retryTimes
	     * @param sleepMillis
	     * @return
	     */
	    public boolean lock(String key,String requestID, long expireSeconds, int retryTimes, long sleepMillis,String serverName) {
	 		
	 		return redisDistributedLock.lock(key,requestID, expireSeconds,retryTimes,sleepMillis,serverName);
	 	}
	    /**
	     * 分布式锁释放锁
	     * @param key
	     * @param requestID 请求唯一标识，是在加锁时生成的。
	     * @param serverName 服务名称。
	     * @return
	     */
	 	public boolean releaseLock(String key,String requestID,String serverName) {
	 		return redisDistributedLock.releaseLock(key, requestID,serverName);
	 	}
	 	/**redis序列号
	     * @param key
	     * @param expireTime <i>过期时间</i>
	     * @return
	     */
	    public  long getIncrementSeqNo(String key,Date expireTime) {
	        //RedisAtomicLong为原子类，根据传入的key和redis链接工厂创建原子类
	        RedisAtomicLong counter = new RedisAtomicLong(key,redisTemplate.getConnectionFactory());
	        //设置过期时间
	        counter.expireAt(expireTime);
	        return counter.incrementAndGet();
	        
	    }
   
}
